using System.Collections.Generic;
using System.Linq;
using Engine;
using Engine.Graphics;
using Engine.OldSerialization;
using Game;

namespace RuthlessConquest {
    public class Ship : Entity {
        public static DynamicArray<Body> TmpBodyList = new();
        public static int MaxShipCapacityOfPlanet = 999;
        public int RouteIndex;
        public Body Body { get; }

        public Point2 Position {
            get => Body.Position;
            set => Body.Position = value;
        }

        public Point2 Velocity { get; set; }
        public Faction Faction { get; set; }
        public Faction GiftToFaction { get; set; }
        public Planet Source { get; set; }
        public DynamicArray<Planet> Route { get; } = new();

        public Ship() {
            Body = new Body();
            Body.Tag = this;
        }

        public Ship(Faction faction, Faction giftToFaction, Point2 position, Planet source, IEnumerable<Planet> route) : this() {
            Faction = faction;
            GiftToFaction = giftToFaction;
            Position = position;
            Source = source;
            Route.AddRange(route);
        }

        public override void Serialize(InputArchive archive) {
            archive.Serialize("Body", Body);
            Velocity = archive.Serialize<Point2>("Velocity");
            Faction = archive.Serialize<Faction>("Faction");
            GiftToFaction = archive.Serialize<Faction>("GiftToFaction");
            Source = archive.Serialize<Planet>("Source");
            RouteIndex = archive.Serialize<int>("RouteIndex");
            archive.SerializeCollection("Route", Route);
        }

        public override void Serialize(OutputArchive archive) {
            archive.Serialize("Body", Body);
            archive.Serialize("Velocity", Velocity);
            archive.Serialize("Faction", Faction);
            archive.Serialize("GiftToFaction", GiftToFaction);
            archive.Serialize("Source", Source);
            archive.Serialize("RouteIndex", RouteIndex);
            archive.SerializeCollection("Route", "Planet", Route);
        }

        public override void OnAdded() {
            Game.ShipsModule.Ships.Add(this);
            Game.ShipsModule.FactionOwnShips[GiftToFaction == Faction.None ? (int)Faction : (int)GiftToFaction]++;
            Body.GridRadius = 90;
            Body.RepelFactor = Game.CreationParameters.ShipRepelFactor;
        }

        public override void OnRemoved() {
            Game.ShipsModule.Ships.Remove(this);
            Game.ShipsModule.FactionOwnShips[GiftToFaction == Faction.None ? (int)Faction : (int)GiftToFaction]--;
        }

        public void Step() {
            Point2 zero = Point2.Zero;
            Planet planet = Route.Count > RouteIndex ? Route[RouteIndex] : null;
            Planet planet2 = Route.Count > 0 ? Route[Route.Count - 1] : null;
            TmpBodyList.Clear();
            BodiesModule.QueryBodies(Position, Body.GridRadius, TmpBodyList, 10);
            foreach (Body tmpBody in TmpBodyList) {
                if (tmpBody == Body
                    || tmpBody.Tag == Source
                    || tmpBody.Tag == planet2
                    || (tmpBody.Tag == planet && tmpBody.Tag is Planet planet3 && planet3.Faction != Faction)) {
                    continue;
                }
                if (tmpBody.Tag is Ship ship
                    && ship.Faction != Faction
                    && IntMath.ApproxDistance(Position, ship.Position) < 2 * Body.GridRadius) {
                    RemoveShip(true, new Vector2(Velocity));
                    ship.RemoveShip(true, new Vector2(ship.Velocity));
                    return;
                }
                int num = tmpBody.GridRadius * 6 / 10;
                int gridRadius = tmpBody.GridRadius;
                Point2 point = tmpBody.Position - Position;
                int num2 = IntMath.ApproxLength(point);
                int num3 = MathUtils.Clamp(-100 * (num2 - Body.GridRadius - gridRadius) / (gridRadius - num), 0, 100);
                if (num2 > 0) {
                    zero -= num3 * point / num2 * tmpBody.RepelFactor / 32;
                }
                if (!(tmpBody.Tag is Planet)) {
                    continue;
                }
                Point2 point2 = planet != null ? planet.Position - Position : Point2.Zero;
                if (point2.X * point.X + point2.Y * point.Y >= 0) {
                    Point2 point3 = point2.X * point.Y - point2.Y * point.X > 0 ? -new Point2(point2.Y, -point2.X) : new Point2(point2.Y, -point2.X);
                    int num4 = IntMath.ApproxLength(point3);
                    if (num4 > 0) {
                        zero -= num3 * point3 / num4 * tmpBody.RepelFactor / 20;
                    }
                }
            }
            if (planet != null) {
                Point2 point4 = planet.Position - Position;
                int num5 = IntMath.ApproxLength(point4);
                if (planet == planet2
                    || planet.Faction != Faction) {
                    int num6 = planet.Radius + 20;
                    if (num5 <= num6) {
                        EnterPlanet(planet);
                        return;
                    }
                }
                else {
                    int num7 = planet.Radius + 50;
                    if (num5 <= 2 * num7) {
                        RouteIndex++;
                    }
                }
                if (num5 > 0) {
                    zero += 100 * point4 / num5;
                }
            }
            int num8 = 800;
            Velocity += zero * num8 / 1024;
            int num9 = IntMath.ApproxLength(Velocity);
            if (num9 > Game.CreationParameters.ShipSpeed) {
                Velocity = Velocity * Game.CreationParameters.ShipSpeed / num9;
            }
            Position += Velocity * 17 / 1024;
        }

        public void Draw(Color colorTransform) {
            TexturedBatch2D texturedBatch2D = CameraModule.PrimitivesRenderer.TexturedBatch(
                GetTexture(Faction),
                false,
                0,
                null,
                null,
                BlendState.AlphaBlend
            );
            Vector2 vector = new(Position);
            Vector2 vector2 = Vector2.Normalize(new Vector2(Velocity)) * 100f;
            Vector2 vector3 = Vector2.Perpendicular(vector2);
            Vector2 vector4 = vector - vector3 + vector2;
            Vector2 p = vector + vector3 + vector2;
            Vector2 vector5 = vector + vector3 - vector2;
            Vector2 p2 = vector - vector3 - vector2;
            Color color = GetColor() * colorTransform;
            texturedBatch2D.QueueTriangle(
                vector4,
                p,
                vector5,
                0f,
                new Vector2(0f, 0f),
                new Vector2(1f, 0f),
                new Vector2(1f, 1f),
                color
            );
            texturedBatch2D.QueueTriangle(
                vector5,
                p2,
                vector4,
                0f,
                new Vector2(1f, 1f),
                new Vector2(0f, 1f),
                new Vector2(0f, 0f),
                color
            );
        }

        public void RemoveShip(bool explode, Vector2 velocity) {
            if (Game != null) {
                if (explode && ParticlesModule.ParticleSystems.Count(p => p is ExplosionParticleSystem) < 75) {
                    ParticlesModule.AddParticleSystem(new ExplosionParticleSystem(new Vector2(Position), velocity, 1f, 1f, GetColor()));
                }
                Game.RemoveEntity(this);
            }
        }

        public static string GetShipRangeName(ShipRange shipRange) {
            int index = shipRange switch {
                ShipRange.Tiny => 1,
                ShipRange.Mini => 2,
                ShipRange.Short => 3,
                ShipRange.Medium => 4,
                ShipRange.Long => 5,
                ShipRange.VeryLong => 6,
                ShipRange.Unlimited => 7,
                _ => 0
            };
            return $"{LanguageControl.Get("RCShipRangeName", index)} ({(int)shipRange})";
        }

        public static Color GetColor(Faction faction) => faction == Faction.Neutral ? Color.White : Planet.GetColor(faction);

        public static Texture2D GetTexture(Faction faction) => faction switch {
            Faction.Faction1 => Textures.Ship1,
            Faction.Faction2 => Textures.Ship2,
            Faction.Faction3 => Textures.Ship3,
            Faction.Faction4 => Textures.Ship4,
            Faction.Faction5 => Textures.Ship5,
            Faction.Faction6 => Textures.Ship6,
            Faction.Neutral => Textures.Ship7,
            _ => Textures.Ship1
        };

        public Color GetColor() {
            if (GiftToFaction != Faction.None) {
                return GetColor(GiftToFaction);
            }
            return GetColor(Faction);
        }

        public void EnterPlanet(Planet planet) {
            if (planet.Faction == Faction
                || planet.Faction == GiftToFaction) {
                planet.ShipsCount = MathUtils.Min(planet.ShipsCount + 1, Planet.MaxShipsCapacity);
                RemoveShip(false, Vector2.Zero);
            }
            else if (planet.ShipsCount >= 1) {
                if (StepModule.Random.Int(100) < planet.DefenceFactor) {
                    int shipsCount = planet.ShipsCount - 1;
                    planet.ShipsCount = shipsCount;
                }
                RemoveShip(true, new Vector2(-Velocity));
            }
            else {
                planet.ChangeOwner(GiftToFaction == Faction.None ? Faction : GiftToFaction);
                RemoveShip(false, Vector2.Zero);
            }
        }
    }
}